package com.xxxx.car.flow.service.impl;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.xxxx.car.audit.domain.PackageAudit;
import com.xxxx.car.audit.vo.HandleAuditParam;
import com.xxxx.car.audit.vo.HistoryTask;
import com.xxxx.car.common.core.domain.AjaxResult;
import com.xxxx.car.common.utils.DateUtils;
import com.xxxx.car.common.utils.SecurityUtils;
import com.xxxx.car.common.utils.StringUtils;
import com.xxxx.car.common.utils.file.FileUtils;
import com.xxxx.car.flow.domain.BpmnInfo;
import com.xxxx.car.flow.service.IProcessService;
import io.jsonwebtoken.lang.Assert;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

@Service
public class ProcessServiceImpl implements IProcessService {

    public static final String ZIP_FILE_NAME = "zip";
    public static final String BPMN_FILE_NAME = "bpmn";
    public static final String XML_FILE_NAME = "xml";
    public static final List<String> ALLOW_EXT = Arrays.asList(ZIP_FILE_NAME,BPMN_FILE_NAME,XML_FILE_NAME);

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    @Override
    public BpmnInfo depoly(String bpmnFileName, InputStream bpmnFileSteam) {
        // 文件类型: xxx.bpmn / xxx.zip
        String suffix = FileUtils.getFileSuffix(bpmnFileName);
        Assert.state(ALLOW_EXT.contains(suffix),"文件上传格式有误,仅支持 bpmn/zip/xml 格式的文件");

        /*
        *  出现问题: org.activiti.bpmn.exceptions.XMLException: cvc-complex-type.2.4.a: 发现了以元素 ‘process’ 开头的无效内容。
        *  添加此方法 disableSchemaValidation()
        * */
        DeploymentBuilder deployment = repositoryService.createDeployment().disableSchemaValidation();
        // 判断文件类型
        if(ZIP_FILE_NAME.equals(suffix)){
            // zip 压缩包的文件
            deployment.addZipInputStream(new ZipInputStream(bpmnFileSteam));
        }else {
            // 普通的bpmn 文件
            deployment.addInputStream(bpmnFileName,bpmnFileSteam);
        }
        // 结果对象
        BpmnInfo result = new BpmnInfo();
        // 部署流程定义
        Deployment deploy = deployment.deploy();
        result.setDeployTime(deploy.getDeploymentTime());
        // 查询流程定义数据
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();

        Assert.notNull(definition,"部署资源异常");

        result.setBpmnLabel(definition.getName());
        result.setProcessDefinitionKey(definition.getKey());
        result.setVersion((long) definition.getVersion());

        return result;
    }

    @Override
    public InputStream getResourceAsStream(String processDefinitionKey, Long version, String type) {
        // 1.获取流程定义对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .processDefinitionVersion(version.intValue())
                .singleResult();

        // 2.获取流程资源文件
        if ("xml".equals(type)) {
            // 获取流程资源文件
            return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        }
        // 3.获取图片资源
        if (StringUtils.isNotEmpty(processDefinition.getDiagramResourceName())) {
            return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getDiagramResourceName());
        }

        // 4.动态生成流程图
        ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
        // 获取指定流程定义的 bpmn model 对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        // 生成流程图, 指定模型对象与字体
        return generator.generateDiagram(bpmnModel,"宋体","宋体","宋体");
    }

    @Override
    public void deleteDefinition(String processDefinitionKey, Long version) {
        // 查询到流程定义对象
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .processDefinitionVersion(version.intValue())
                .singleResult();
        // 调用 repositoryService 进行删除
        repositoryService.deleteDeployment(definition.getDeploymentId());
    }

    @Override
    public BpmnModel getBpmnModel(String definitionKey, int version) {
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(definitionKey)
                .processDefinitionVersion(version)
                .singleResult();

        return repositoryService.getBpmnModel(definition.getId());
    }

    @Override
    public String startProcessInstance(String definitionKey, String businessKey, HashMap<String, Object> variables) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(definitionKey, businessKey, variables);
        return processInstance.getId();
    }

    @Override
    public void deleteProcessInstance(String instanceId, String reason) {
        runtimeService.deleteProcessInstance(instanceId,reason);
    }

    @Override
    public List<String> selectBusinessKeyList(String processDefinitionKey, Long userId) {
        // 1.基于流程定义key + 版本以及当前用户id作为负责人, 查询任务列表 => TaskService
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(processDefinitionKey)
                .taskAssignee(String.valueOf(userId))
                .list();
        // 如果查询不到待办任务,直接返回空数据
        if(CollectionUtils.isEmpty(list)){
            return Collections.emptyList();
        }
        // 2.通过任务列表得到流程实例id列表 (去重)
        Set<String> instanceIdList = new HashSet<>();
        for (Task task : list) {
            instanceIdList.add(task.getProcessInstanceId());
        }
        // 3.基于流程实例id列表,查询流程实例列表,得到业务标识列表 => RuntimeService
        List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
                .processInstanceIds(instanceIdList)
                .list();
        List<String> businessKey = new ArrayList<>();
        for(ProcessInstance instance : instances){
            businessKey.add(instance.getBusinessKey());
        }
        return businessKey;
    }

    @Override
    public boolean handleTaskByInstanceIdAndAssignee(String instanceId, Long userId, HandleAuditParam param) {
        // 1.基于流程实例id + 当前用户id 查询待办任务
        Task task = taskService.createTaskQuery()
                .processInstanceId(instanceId)
                .taskAssignee(String.valueOf(userId))
                .singleResult();
        Assert.notNull(task,"处理失败,任务不存在");
        // 2.处理任务(流程变量) , 添加批注(根据状态添加)、
        HashMap<String, Object> variables = new HashMap<>();
        // 是否通过
        variables.put("shopOwnerAudit",param.isPassed());
        // 添加批注
        String username = SecurityUtils.getUsername();
        String msg = username + "[" + (param.isPassed() ? "通过" : "拒绝") + "], " + param.getInfo();
        taskService.addComment(task.getId(),instanceId,msg);
        // 完成任务
        taskService.complete(task.getId(),variables);
        // 3.查询是否还有下一个节点
        ProcessInstance instance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(instanceId)
                .singleResult();
        return instance == null;
    }

    @Override
    public List<String> selectHistoryBusinessKeyList(String processDefinitionKey, Long userId,String type) {
        // 1.根据流程信息对象 + 当前用户查询已完成的历史任务
        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .taskAssignee(userId + "");
        if(PackageAudit.DONE_LIST.equals(type)){
            taskInstanceQuery.finished();
        }else {
            taskInstanceQuery.unfinished();
        }
        List<HistoricTaskInstance> historyTask = taskInstanceQuery.list();
        // 2.如果查不到, 直接返回空集合
        if(CollectionUtils.isEmpty(historyTask)){
            return Collections.emptyList();
        }
        // 3.将任务对象转换为流程实例id集合
        Set<String> processInstanceIds = historyTask.stream()
                .map(HistoricTaskInstance::getProcessInstanceId)
                .collect(Collectors.toSet());
        // 4.根据流程实例id集合,查询流程实例集合
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(processInstanceIds)
                .list();
        // 5.将流程实例集合,转换为businessKey 集合
        return historicProcessInstances.stream()
                .map(HistoricProcessInstance::getBusinessKey)
                .collect(Collectors.toList());
    }

    @Override
    public List<HistoryTask> selectHistoryTaskListByInstanceId(String instanceId) {
        // 1.基于流程实例id 查询所有历史任务
        List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(instanceId)
                .list();
        // 2.判断为空返回空集合
        if(CollectionUtils.isEmpty(taskInstanceList)){
            return Collections.emptyList();
        }
        // 3.将Activiti 的任务模型对象转换为自己封装的对象
        // 4.返回集合
        return taskInstanceList.stream()
                .map(t -> {
                    HistoryTask task = new HistoryTask();
                    if(t.getDurationInMillis() != null) {
                        task.setDuration(DateUtils.timeDistance(t.getDurationInMillis()));
                    }
                    task.setEndTime(t.getEndTime());
                    task.setStartTime(t.getStartTime());
                    task.setName(t.getName());
                    String reason = t.getDeleteReason();
                    if(StringUtils.isNotEmpty(reason)){
                        task.setComment(reason);
                    }else {
                        // 查询批注
                        List<Comment> comments = taskService.getTaskComments(t.getId());
                        if(!CollectionUtils.isEmpty(comments)){
                            task.setComment(comments.get(0).getFullMessage());
                        }
                    }
                    return task;
                }).collect(Collectors.toList());
    }

    @Override
    public InputStream getProcessingImageByInstanceId(String instanceId) {
        // 1. 基于流程实例id 查询流程实例对象
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId)
                .singleResult();
        Assert.notNull(processInstance,"参数错误,流程实例不存在");
        // 2.基于流程定义id查询 BpmnModel 对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        // 3.查询当前流程实例高亮活动id集合
        // List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery()
        //         .processInstanceId(instanceId)
        //         .activityType("userTask")
        //         .unfinished()
        //         .list();
        //
        // List<String> actIds = new ArrayList<>();
        // for(HistoricActivityInstance act : activityInstances){
        //     actIds.add(act.getActivityId());
        // }
        List<String> activeActivityIds = null;
        // 判断流程是否没有结束
        if(processInstance.getEndTime() == null){
            activeActivityIds = runtimeService.getActiveActivityIds(instanceId);
        }else {
            activeActivityIds = Collections.emptyList();
        }
        if(activeActivityIds == null){
            activeActivityIds = Collections.emptyList();
        }
        // 4.生成图片 (BpmnModel 高亮的活动id集合,字体)
        return new DefaultProcessDiagramGenerator()
                .generateDiagram(bpmnModel,activeActivityIds,Collections.emptyList(),"宋体","宋体","宋体");
    }
}
